#pragma once

#include "Enum.h"
#include <list>
#include <fstream>
#include <set>

class Tile;
class Npc;
class Dungeon;
class Portal;
class Player;
class Character;

struct AStarTile {
	Tile* tile;
	AStarTile* parent;
	int f, g, h;
};

class Level {
protected:
	int m_rows;
	int m_cols;
	int m_levelNum;
	int m_dlevel;
	std::list<Npc*>* m_npcs;
	std::list<Portal*> m_mainUpPortals;
	std::list<Portal*> m_mainDownPortals;
	Tile*** m_map;
	Dungeon* m_dungeon;
	void generateLevel();
	virtual void generateLayout();
	virtual void generateCharacters();
	virtual void generateObjects();
	void placePortal(bool up, int portalNum);
	static AStarTile* generateAStarTile(Tile* tile, AStarTile* parent, int endx, int endy, Character* c);
	static void insertSort(std::list<AStarTile*> &tiles, AStarTile* tile);
	static AStarTile* aStarListContains(std::list<AStarTile*> &tiles, Tile* tile);
	static int generateG(AStarTile* tile, int endx, int endy, Character* c);
	bool lineOfSight(int charX, int charY, int endX, int endY, int viewRadius) const;
public:
	Level(int cols, int rows, int levelNum, int dlevel, Dungeon* dungeon);
	Level(int cols, int rows, Level* level);
	Level(Dungeon* dungeon, std::ifstream& file);
	~Level();
	void placePlayer(Player* player, bool up, int portalNum);
	Tile* getTile(int x, int y) const;
	int getRows() const;
	int getCols() const;
	std::list<Npc*>* getNpcs() const;
	int getDLevel() const;
	Dungeon* getDungeon() const;
	int getLevelNum() const;
	int getNumPortals(bool up) const;
	void removeNpc(Npc* npc);
	std::list<Tile*>* findPath(int startx, int starty, int endx, int endy, Character* c) const;
	void addNpc(Npc* n, int x, int y);
	void save(std::ofstream& file) const;
	void loadPortal(Portal* portal);
	void calculateFOV(int charX, int charY, int viewRadius, std::set<Tile*>& visibleTiles) const;
};

inline Tile* Level::getTile(int x, int y) const {return m_map[x][y];}
inline int Level::getRows() const {return m_rows;}
inline int Level::getCols() const {return m_cols;}
inline std::list<Npc*>* Level::getNpcs() const {return m_npcs;}
inline int Level::getDLevel() const {return m_dlevel;}
inline Dungeon* Level::getDungeon() const {return m_dungeon;}
inline int Level::getLevelNum() const {return m_levelNum;}
inline int Level::getNumPortals(bool up) const {return int(up ? m_mainUpPortals.size() : m_mainDownPortals.size());}
